package com.pi.shisan.service.config;

import com.pi.shisan.service.annotation.CalcNextTimeType;
import com.pi.shisan.service.util.CalcNextTimeRegister;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.util.Assert;

import java.util.LinkedHashSet;
import java.util.Set;

@Slf4j
public class AnnotationScannerConfigurer implements ApplicationContextAware, BeanDefinitionRegistryPostProcessor {

    private ApplicationContext applicationContext;


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        CalcNextClockScanner annotationScanner = new CalcNextClockScanner(registry);
        annotationScanner.setResourceLoader(applicationContext);
        //务必放在该目录下才会生效
        annotationScanner.scan("com.pi.shisan.service.service");
    }


    public class CalcNextClockScanner extends ClassPathBeanDefinitionScanner {
        public CalcNextClockScanner(BeanDefinitionRegistry registry) {
            super(registry);
        }

        @Override
        protected void registerDefaultFilters() {
            this.addIncludeFilter(
                    (metadataReader, metadataReaderFactory)-> hasInterface(metadataReader)
            );
        }

        @Override
        protected Set<BeanDefinitionHolder> doScan(String... basePackages){
            Set<BeanDefinitionHolder> beanDefinitionHolders = templateScan(basePackages);
            for(BeanDefinitionHolder beanDefinitionHolder:beanDefinitionHolders){
                BeanDefinition beanDefinition = beanDefinitionHolder.getBeanDefinition();
                try{
                    Class<?> tabContentGeneratorClass = Class.forName(beanDefinition.getBeanClassName());
                    CalcNextTimeType annotation = tabContentGeneratorClass.getAnnotation(CalcNextTimeType.class);
                    CalcNextTimeRegister.register(annotation, beanDefinition.getBeanClassName());
                }catch (Exception e){
                    log.error("注册template失败：",e);
                    throw new RuntimeException(e);
                }
            }
            return beanDefinitionHolders;
        }

        private boolean hasInterface(MetadataReader metadataReader){
            String[] interfaceNames = metadataReader.getClassMetadata().getInterfaceNames();
            if(interfaceNames == null || interfaceNames.length == 0){
                return false;
            }
            boolean foundInterface = false;
            for(String s:interfaceNames){
                if(foundInterface = s.equals("com.pi.shisan.service.service.ClockNextTimeService")){
                    break;
                }
            }
            if(foundInterface){
                return metadataReader.getAnnotationMetadata().hasAnnotation("com.pi.shisan.service.annotation.CalcNextTimeType");
            }
            return false;
        }

        private Set<BeanDefinitionHolder> templateScan(String... basePackages){
            Assert.notEmpty(basePackages, "At least one base package must be specified");
            Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
            for (String basePackage : basePackages) {
                Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
                for (BeanDefinition candidate : candidates) {
                    candidate.setScope(BeanDefinition.SCOPE_SINGLETON);
                    //使用类全路径作为beanId
                    String beanName = candidate.getBeanClassName();
                    if (candidate instanceof AbstractBeanDefinition) {
                        postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
                    }
                    if (candidate instanceof AnnotatedBeanDefinition) {
                        AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
                    }
                    if (checkCandidate(beanName, candidate)) {
                        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
                        beanDefinitions.add(definitionHolder);
                        registerBeanDefinition(definitionHolder, getRegistry());
                    }
                }
            }
            return beanDefinitions;
        }
    }
}